package com.glsc.ngateway.platform.service.itflow.flowstrategy;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.common.api.common.enums.DictConstant;
import com.glsc.ngateway.common.api.common.enums.PlatformConstant;
import com.glsc.ngateway.common.api.flowable.dto.FlowCommonTaskDto;
import com.glsc.ngateway.common.api.flowable.dto.FlowDoTaskRespDto;
import com.glsc.ngateway.common.api.flowable.dto.FlowStartupFormDto;
import com.glsc.ngateway.common.api.flowable.dto.param.ParamCreateDto;
import com.glsc.ngateway.common.api.flowable.dto.param.ParamDeleteDto;
import com.glsc.ngateway.common.api.flowable.dto.param.ParamDoTaskDto;
import com.glsc.ngateway.common.api.flowable.dto.param.ParamRollbackDto;
import com.glsc.ngateway.common.api.flowable.dto.task.FlowFormBaseEntityDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.AbstractCreateParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.AbstractFlowSearchParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.AbstractPassParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.doTaskParam.AbstractRejectParamDto;
import com.glsc.ngateway.common.api.flowable.dto.task.formData.AbstractTaskFormDataDto;
import com.glsc.ngateway.common.api.flowable.feign.IFeignFlowableService;
import com.glsc.ngateway.common.api.flowable.resp.FlowableResponse;
import com.glsc.ngateway.common.api.flowable.resp.ResponseCreateDto;
import com.glsc.ngateway.common.api.flowable.resp.ResponseMsgDto;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrDeptDto;
import com.glsc.ngateway.common.api.ldap.feign.IFeignLdapService;
import com.glsc.ngateway.common.api.ldap.resp.AppEhrResponse;
import com.glsc.ngateway.common.api.oaflow.feign.IFeignOaFlowService;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.base.domain.mysql.gateway.ldap.LdapEhrDept;
import com.glsc.ngateway.common.base.domain.mysql.gateway.oaflow.TOaFlowSyncLog;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowStatus;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.flow.FlowPushOaLog;
import com.glsc.ngateway.common.base.enums.itflow.FlowConfigConfigTypeEnum;
import com.glsc.ngateway.common.base.enums.itflow.FlowConfigValueTypeEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowEnum;
import com.glsc.ngateway.common.base.enums.itflow.ITFlowStatus;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.platform.service.FlowCommonService;
import com.glsc.ngateway.platform.service.UserService;
import com.glsc.ngateway.platform.service.itflow.config.ItFlowConfigService;
import com.glsc.ngateway.platform.utils.JsonTool;
import lombok.SneakyThrows;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author xc
 * @date 2023/01/11
 * 设备流程策略模板类
 */
public abstract class AbstractItFlowStrategy<D extends FlowFormBaseEntityDto, T extends AbstractTaskFormDataDto<D>> {

    Logger logger = LoggerFactory.getLogger(AbstractItFlowStrategy.class);

    @Resource
    private UserService userService;

    @Resource
    private IFeignFlowableService feignFlowableService;

    @Resource
    @Qualifier("transactionManagerMysql")
    private PlatformTransactionManager transactionManager;

    @Resource
    private FlowCommonService flowCommonService;

    @Autowired
    private IFeignOaFlowService oaFlowService;

    @Value("${syncFlow.workflowId}")
    private String workflowId;

//    @Autowired
//    private HrmdepartmentRepository deptRepo;

//    @Autowired
//    private HrmsubcompanyRepository companyRepo;

    @Resource
    private IFeignLdapService iFeignLdapService;

    @Autowired
    private ItFlowConfigService flowConfigService;

    /**
     * 创建流程
     */
    @SneakyThrows
    public FlowDoTaskRespDto createFlow(@NotNull AbstractCreateParamDto<D, T> param, @NotNull String operatorStart) {
        //保存草稿时不需要检查
        if (StrUtil.isBlank(param.getCreateToTaskDefinitionKey())
                || !param.getCreateToTaskDefinitionKey().equals(FlowCommonTaskDto.BEGIN_TASK)) {
            this.checkDataBeforeCreateFlow(param);
        }
        PlatformUserDto createUser = userService.findByUsername(operatorStart);
        if (Objects.isNull(createUser)) {
            throw PlatformException.error("未找到流程发起人信息", false, false);
        }
        D formMainData = param.getFlowableFormData().getFormMainData();
        formMainData.setProcessCreateTime(LocalDateTime.now());
        formMainData.setFlowCreateOperatorAccount(createUser.getUsername());
        formMainData.setFlowCreateOperatorName(createUser.getName());
        formMainData.setFlowCreatorDepartment(createUser.getUserDepartment());
        formMainData.setCreateAccount(operatorStart);
        formMainData.setUpdateAccount(operatorStart);
        AppEhrResponse<LdapEhrDeptDto> ldapResp = iFeignLdapService.getDepByAccount(createUser.getUsername());
        if (Objects.isNull(ldapResp) || Objects.isNull(ldapResp.getData())) {
            throw PlatformException.error("未找到部门信息" + createUser.getUserDepartment());
        }
        formMainData.setFlowCreatorCompany(ldapResp.getData().getComptitle());
        //【流程节点】，动态指定节点处理人，
        //初始节点，需要额外加载代码逻辑
        Map<String, String> taskAssignee = this.initTaskUserGroup(this.initDefaultTaskUserGroup(createUser));
        //【流程节点变量】
        //初始节点，需要额外加载代码逻辑
        Map<String, String> conditionalVariable = this.initTaskConditionalVar(this.initDefaultTaskConditionalVar(taskAssignee, createUser));
        TransactionStatus status = null;
        FlowDoTaskRespDto createFlowResponse = null;
        T flowableForm = param.getFlowableFormData();
        try {
            // 事务定义x
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 每次都开启一个新事务
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 获取事务状态
            status = transactionManager.getTransaction(def);
            //2.表单内容（发起流程，表单内容为辅助）
            FlowStartupFormDto formDto = new FlowStartupFormDto();
            formDto.setTitle(flowableForm.getProcessTitle());
            //formDto.setComment("xx");//发起备注
            formDto.setCreatorId(createUser.getUserid());
            formDto.setCreatorName(createUser.getUsername());

            ParamCreateDto paramCreateDto = new ParamCreateDto();
            paramCreateDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
            paramCreateDto.setProcessDefinitionKey(this.getFlowEnum().getKey());
            //【重要！】这字段标识发起人！！！
            paramCreateDto.setUserid(operatorStart);
            //纯粹为了flowable创建content需要一个JSON表单数据
            paramCreateDto.setContent(JSONObject.toJSON(formDto).toString());
            //各节点处理人
            paramCreateDto.setTaskAssignee(taskAssignee);
            //控制流程节点变量
            paramCreateDto.setConditionalVariable(conditionalVariable);
            //评论附言
            if (Objects.nonNull(param.getCommentDto())) {
                paramCreateDto.setCreateComment(param.getCommentDto().getFullMessage());
            }
            //未启流程保存草稿时，CreateToTaskDefinitionKey变量为"beginTask"，流程引擎会在创建后调到初始节点
            if (StrUtil.isNotBlank(param.getCreateToTaskDefinitionKey())) {
                paramCreateDto.setCreateToTaskDefinitionKey(param.getCreateToTaskDefinitionKey());
            }
            createFlowResponse = new FlowDoTaskRespDto();
            //4.微服务创建flowable流程
            FlowableResponse<ResponseCreateDto> createFlowableFlowResponse = feignFlowableService.create(paramCreateDto);
            if (createFlowableFlowResponse.getStatus() == 0) {
                createFlowResponse.setProcessId(createFlowableFlowResponse.getData().getProcessId());
                param.setProcessId(createFlowableFlowResponse.getData().getProcessId());
                logger.info("流程关联，{}", createFlowResponse);
            } else {
                throw PlatformException.error(String.format("流程创建失败，{%s}，错误码={%s}-，原因：{%s}",
                        flowableForm.getProcessTitle(), createFlowableFlowResponse.getStatus(), createFlowableFlowResponse.getMsg()));
            }
            //流程表单个性化要素保存（设置关联的流程）
            flowableForm.setProcessId(createFlowResponse.getProcessId());
            //流程业务数据状态初始化
            flowableForm.getFormMainData().setStatus(ITFlowStatus.NORMAL.getCode());
            T flowableFormData = this.saveOrUpdateForm(flowableForm, operatorStart);
            //创建流程后置处理
            this.createAfterHandle(param, operatorStart);
            //提交事务
            transactionManager.commit(status);
            return createFlowResponse;
        } catch (Exception e) {
            logger.error(e.getMessage());
            //flowable回滚
            if (Objects.nonNull(createFlowResponse) && Objects.nonNull(createFlowResponse.getProcessId())) {
                logger.error("流程删除:" + createFlowResponse.getProcessId());
                feignFlowableService.deleteProcess(ParamDeleteDto.builder().processIdList(Collections.singletonList(createFlowResponse.getProcessId())).build());
            }
            if (Objects.nonNull(status)) {
                transactionManager.rollback(status);
            }
            throw e;
        }
    }

    /**
     * 审核通过
     */
    public <P extends AbstractPassParamDto<D, T>> FlowDoTaskRespDto taskAuditPass(@NotNull P param, @NotNull String operatorStart) throws Exception {
        return this.taskAuditPass(param, operatorStart, null);
    }

    /**
     * @param customAfterHandler 指定后置处理方法
     */
    public <P extends AbstractPassParamDto<D, T>> FlowDoTaskRespDto taskAuditPass(@NotNull P param, @NotNull String operatorStart, @Nullable IItFlowCustomPassAfterHandler customAfterHandler) throws Exception {
        this.checkDataBeforePassTask(param);
        //【1.任务合法性数据】
        FlowDoTaskRespDto respDto = flowCommonService.validCheckTaskBeforeSaveFormOrAuditTask(param.getTaskId());
        //根据查询出的节点类型赋值
        param.setProcessDefinitionKey(respDto.getProcessDefineKey());
        param.setTaskDefinitionKey(respDto.getTaskDefinitionKey());
        T flowInfo = findFormByProcessId(param.getProcessId());
        String createAccount = flowInfo.getFormMainData().getCreateAccount();
        PlatformUserDto createUser = userService.findByUsername(createAccount);
        //流程注入变量更新
        //【流程节点】，动态指定节点处理人，
        Map<String, String> taskAssignee = this.initTaskUserGroup(this.initDefaultTaskUserGroup(createUser));
        //【流程节点变量】
        Map<String, String> conditionalVariable = this.initTaskConditionalVar(this.initDefaultTaskConditionalVar(taskAssignee, createUser));
        //审核事务通过
        TransactionStatus status = null;
        FlowableResponse<ResponseMsgDto> flowableResponse;
        try {
            // 事务定义
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            // 每次都开启一个新事务
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            // 获取事务状态
            status = transactionManager.getTransaction(def);
            //审核流程通过，组织表单内容、审核注入变量、评论等
            ParamDoTaskDto paramDotaskDto = new ParamDoTaskDto();
            paramDotaskDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
            paramDotaskDto.setTaskId(param.getTaskId());
            paramDotaskDto.setComment(flowCommonService.initCommentDto(param.getCommentDto()));
            paramDotaskDto.setTitle(param.getProcessTitle());
            paramDotaskDto.setContent(JsonTool.objectToJson(paramDotaskDto));//flowable网关保存每次审批的全量数据
            //重置审批人
            paramDotaskDto.setTaskAssignee(taskAssignee);
            //重置变量
            conditionalVariable.put("rateLeaderTaskHas",isDepLeader(createAccount));
            paramDotaskDto.setConditionalVariable(conditionalVariable);
            paramDotaskDto.setComment(param.getCommentDto());
            //TODO: 用TCC完成事务
            //调用网关审核，注意前面表单的保存与这里审批通过，事物可能不一致，暂时不予处理这种不一致的情况
            flowableResponse = flowCommonService.taskAuditPass(paramDotaskDto);
            if (0 == flowableResponse.getStatus()) {
                //流程审批后置处理，如果指定方法，则用指定方法
                if (Objects.nonNull(customAfterHandler)) {
                    customAfterHandler.passAfterHandle();
                } else {
                    this.auditPassAfterHandle(param, operatorStart);
                }
                //提交事务
                transactionManager.commit(status);
                return respDto;
            } else {
                throw PlatformException.error(String.format("流程审核失败，错误码%s，原因：%s，详情：%s",
                        flowableResponse.getStatus(), flowableResponse.getMsg(), flowableResponse.getData()));
            }
        } catch (Exception e) {
            logger.error(e.getMessage() + ", status:" + status);
            if (status != null) {
                transactionManager.rollback(status);
            }
            throw e;
        }
    }

    /**
     * 流程发起人是否是团队长，
     * 是则直接提交结束，否则提交给团队长审批
     * @param flowCreaterOA
     * @return
     */
    public String isDepLeader(String flowCreaterOA){
        //给节点处理人赋予处理人，如果当前人员是团队长，则跳过团队长审批节点
        AppEhrResponse<LdapEhrDeptDto> depResp = iFeignLdapService.getDepByAccount(flowCreaterOA);
        if (Objects.isNull(depResp) || depResp.getStatus() != 0) {
            throw PlatformException.error("未找到部门信息");
        }
        LdapEhrDept depInfo = JSONObject.parseObject(JSONObject.toJSONString(depResp.getData()), LdapEhrDept.class);
        String leader = depInfo.getLeader();
        //默认员工
        String isDLeader = "1";
        if (!"二级部门".equals(depInfo.getDepgrade()) || Strings.isBlank(leader) || leader.equals(flowCreaterOA)) {
            //特殊情况，团队长审批节点跳过
            isDLeader =  "0";
        }
        return isDLeader;
    }

    /**
     * 驳回
     */
    public <P extends AbstractRejectParamDto> FlowableResponse<ResponseMsgDto> reject(@NotNull P param, @NotNull String operatorStart) {
        return this.reject(param, operatorStart, null);
    }

    /**
     * @param customAfterHandler 指定后置处理方法
     */
    public <P extends AbstractRejectParamDto> FlowableResponse<ResponseMsgDto> reject(@NotNull P param, @NotNull String operatorStart, @Nullable IItFlowCustomRejectAfterHandler customAfterHandler) {
        //任务合法性数据
        flowCommonService.validCheckTaskBeforeSaveFormOrAuditTask(param.getTaskId());
        //TODO【公共处理】查询任务基础信息
        ParamRollbackDto rollbackDto = new ParamRollbackDto();
        T flowInfo = findFormByProcessId(param.getProcessId());
        PlatformUserDto createUser = userService.findByUsername(flowInfo.getFormMainData().getCreateAccount());
        //流程注入变量更新
        //【流程节点】，动态指定节点处理人，
        Map<String, String> taskAssignee = this.initTaskUserGroup(this.initDefaultTaskUserGroup(createUser));
        //【流程节点变量】
        Map<String, String> conditionalVariable = this.initTaskConditionalVar(this.initDefaultTaskConditionalVar(taskAssignee, createUser));
        //任务ID
        rollbackDto.setTaskId(param.getTaskId());
        rollbackDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        rollbackDto.setComment(flowCommonService.initCommentDto(param.getCommentDto()).getFullMessage());
        rollbackDto.setTitle("XXXXX");
        rollbackDto.setTargetTask(param.getTargetTask());
        rollbackDto.setContent(JsonTool.objectToJson(rollbackDto));
        rollbackDto.setTaskAssignee(taskAssignee);
        rollbackDto.setConditionalVariable(conditionalVariable);
        //直接退回至发起人
        FlowableResponse<ResponseMsgDto> flowableResponse = flowCommonService.backToSelect(rollbackDto);
        if (0 != flowableResponse.getStatus()) {
            throw PlatformException.error("流程退回失败:" + flowableResponse.getMsg());
        } else {
            //流程驳回后置处理，如果指定方法，则用指定方法
            if (Objects.nonNull(customAfterHandler)) {
                customAfterHandler.rejectAfterHandle();
            } else {
                this.auditRejectAfterHandle(param, operatorStart);
            }
        }
        return flowableResponse;
    }

    /**
     * 创建流程检查数据(默认不需要)
     */
    protected <A extends AbstractCreateParamDto<D, T>> void checkDataBeforeCreateFlow(A taskDto) throws PlatformException {
    }

    /**
     * 审批流程检查数据(默认不需要)
     */
    protected <A extends AbstractPassParamDto<D, T>> void checkDataBeforePassTask(A passDto) throws PlatformException {
    }

    /**
     * 获取流程审批通过Map<handlerName, Handler>
     */
    protected <Data extends FlowFormBaseEntityDto, D extends AbstractTaskFormDataDto<Data>, P extends AbstractPassParamDto<Data, D>, Handler extends IItFlowAuditPassAfterHandler<Data, D, P>> Map<String, Handler> getTaskPassMap() {
        return Collections.EMPTY_MAP;
    }

    /**
     * 获取流程驳回Map<handlerName, Handler>
     */
    protected <P extends AbstractRejectParamDto, Handler extends IItFlowAuditRejectAfterHandler<P>> Map<String, Handler> getTaskRejectMap() {
        return Collections.EMPTY_MAP;
    }

    /**
     * 流程创建后置处理(默认不需要)
     */
    protected void createAfterHandle(AbstractCreateParamDto<D, T> param, String operator) {
    }

    /**
     * 节点审批后置处理(默认不需要)
     */
    protected <P extends AbstractPassParamDto<D, T>, H extends IItFlowAuditPassAfterHandler<D, T, P>> void auditPassAfterHandle(@NotNull P param, @NotNull String operator) throws Exception {
        Map<String, H> taskMap = getTaskPassMap();
        if (taskMap.containsKey(param.getTaskDefinitionKey())) {
            taskMap.get(param.getTaskDefinitionKey()).passAfterHandle(param, operator);
        }
    }

    /**
     * 节点驳回后置处理（默认不需要）
     */
    protected <P extends AbstractRejectParamDto, H extends IItFlowAuditRejectAfterHandler<P>> void auditRejectAfterHandle(@NotNull P param, String operator) {
        Map<String, H> taskMap = getTaskRejectMap();
        if (taskMap.containsKey(param.getTaskDefinitionKey())) {
            taskMap.get(param.getTaskDefinitionKey()).rejectAfterHandle(param, operator);
        }
    }

    /**
     * 流程类型Id
     */
    public abstract ITFlowEnum getFlowEnum();

    /**
     * 初始化节点流程分配人,默认为空
     */
    public Map<String, String> initTaskUserGroup(@Nullable Map<String, List<String>> overGroupMap) {
        return flowCommonService.initTaskUserGroupListByConfig(getFlowEnum().getKey(),overGroupMap);
    }

    protected Map<String, List<String>> initDefaultTaskUserGroup(PlatformUserDto creator) throws PlatformException {
        return new HashMap<>(0);
    }

    /**
     * 初始化其他变量
     */
    public Map<String, String> initTaskConditionalVar(@Nullable Map<String, String> conditionalVariableMap) {
        return flowCommonService.initTaskConditionalVarByConfig(getFlowEnum().getKey(),conditionalVariableMap);
    }

    /**
     * 初始化其他变量,默认为空
     */
    protected Map<String, String> initDefaultTaskConditionalVar(Map<String, String> taskAssignee, PlatformUserDto createUser) {
        return new HashMap<>(0);
    }

    /**
     * 保存流程表单
     */
    public T saveOrUpdateForm(@NotNull T flowableForm, @NotBlank String operator) throws Exception {
        D formMainData = flowableForm.getFormMainData();
        if (Objects.isNull(flowableForm.getFormMainData())) {
            throw PlatformException.error("数据错误");
        }
        if (Strings.isBlank(formMainData.getProcessId())) {
            formMainData.setCreateAccount(operator);
        }
        formMainData.setUpdateAccount(operator);
        return this.saveOrUpdateForm(flowableForm);
    }

    public T saveOrUpdateForm(@NotNull T flowableForm) throws Exception {
        return flowableForm;
    }

    /**
     * 通过processId获取流程表单信息
     */
    public abstract T findFormByProcessId(String processId);

    /**
     * 分页查询获取processId集合
     */
    public abstract List<String> pageProcessId(AbstractFlowSearchParamDto param, Pageable pageable);

    /**
     * 生成审批要素str，用于同步oaFlow移动审批展示
     */
    public String toSpys(T formData) {
        return Strings.EMPTY;
    }

    /**
     * 从oa侧同步流程
     */
    public void syncFromOaFlow(TOaFlowSyncLog log, String description) throws Exception {
        throw PlatformException.error("从oa侧同步流程error：未找到匹配的策略" + log.getProcessDefKey());
    }

    public void endFromOaFlow(FlowPushOaLog log, String description) throws Exception {
        throw PlatformException.error("响应oa侧结束流程error：未找到匹配的策略" + log.getProcessKey());
    }
    /**
     * 处理OA侧消息，默认不处理
     */
    public void doHandle(@NotBlank String workflowId, @NotBlank String requestId, String description) {
        logger.info(String.format("OA流程：workflowId-%s, requestId-%s，未匹配到规则，不做处理", workflowId, requestId));
    }
}
